/****************************************
File: main.cpp

This file is part of Stellar

This program shows a fictional solar system and
provides several methods of interactions.

Copyright (C) 2001 David Stibbe (dstibbe@gmail.com)
                     and Gerrit Jan Jansen

Stellar is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
****************************************/

#include "framework.h"
#include "opdracht6.h"
#include "planets.h"

GLdouble eyeX;
GLdouble eyeY;
GLdouble eyeZ;
GLdouble centerX;
GLdouble centerY;
GLdouble centerZ;
GLdouble upX;
GLdouble upY;
GLdouble upZ;
GLdouble angle;
GLdouble angle2;

int c[]={0,0,0};
int bx[]={-10,0,0};
int ex[]={10,0,0};
int by[]={0,-10,0};
int ey[]={0,10,0};
int bz[]={0,0,-10};
int ez[]={0,0,100};

int bxby[]={-10,-10,0};
int bxey[]={-10,10,0};
int exey[]={10,10,0};
int exby[]={10,-10,0};

model*			model_list  [MAX_MODELS];
moon*			moon_list   [MAX_MOONS];
planet*			planet_list [MAX_PLANETS];

int				nModels;
int				nMoons;
int				nPlanets;
float			zoomFactor;
float			transFactor;
float			scaleFactor;
float			rotFactor;


void loadModel(int numberOfVerticesC,vertex* verticesC,int numberOfFacesC,face* facesC ){
	model_list[nModels]=new model;

	model_list[nModels]->numberOfVertices=numberOfVerticesC;
    model_list[nModels]->numberOfFaces=numberOfFacesC;
	model_list[nModels]->vertices=verticesC;
	model_list[nModels]->faces=facesC;

	int* con=new int[model_list[nModels]->numberOfVertices];
    int i,j;
	float n[3],e1[3],e2[3];
	float* p1;
	float* p2;
	float* p3;


	for(j=0;j<model_list[nModels]->numberOfVertices;j++)
		con[j]=0;

	for(i=0;i<model_list[nModels]->numberOfFaces;i++){
		p1=verticesC[facesC[i].vertex[0]].pos;
		p2=verticesC[facesC[i].vertex[1]].pos;
		p3=verticesC[facesC[i].vertex[2]].pos;

		con[facesC[i].vertex[0]]++;
		con[facesC[i].vertex[1]]++;
		con[facesC[i].vertex[2]]++;

		for(j=0;j<3;j++){
			e1[j]=p2[j]-p1[j];
			e2[j]=p3[j]-p1[j];
		}

		normalVector(e1,e2,n);


		facesC[i].normal[M_X]=n[M_X];
		facesC[i].normal[M_Y]=n[M_Y];
		facesC[i].normal[M_Z]=n[M_Z];

		for(j=0;j<3;j++){
			verticesC[facesC[i].vertex[j]].normal[M_X]+=n[M_X];
			verticesC[facesC[i].vertex[j]].normal[M_Y]+=n[M_Z];
			verticesC[facesC[i].vertex[j]].normal[M_Z]+=n[M_Z];
		}

	}
	for(i=0;i<model_list[nModels]->numberOfVertices;i++){
		verticesC[i].normal[M_X]/=con[i];
		verticesC[i].normal[M_Y]/=con[i];
		verticesC[i].normal[M_Z]/=con[i];
	}

	nModels++;
}


void mouseButtonDown(float x, float y) {
	printf("mouseButtonDown op (%f, %f)\n", x, y);
}

void mouseButtonUp(float x, float y) {
	printf("mouseButtonUp op (%f, %f)\n", x, y);
}

void mouseDrag(float x, float y) {
	printf("Mousedrag naar (%f, %f)\n", x, y);
}

void doDrawing() {
	plotAxes();
	drawUniverse();
}

void step() {
	moveUniverse();
}

void reset() {
	eyeX=-10;
	eyeY=-10;
	eyeZ=20;
	centerX=0;
	centerY=0;
	centerZ=0;
	upX=0;
	upY=0;
	upZ=1;
	angle=0;
	angle2=0;
	zoomFactor=1;

	glLoadIdentity();
	resetUniverse();

	printf("reset\n");
}

void plotAxes(){
	float rot[3],rot2[3];
	float translation[3]={eyeX,eyeY,eyeZ};

	glPushMatrix();

        glLoadIdentity();

        gluLookAt(zoomFactor*eyeX,zoomFactor*eyeY,zoomFactor*eyeZ,centerX,centerY,centerZ,upX,upY,upZ);


        //rotate the camera x-y
        rotationNormal(translation,rot);
        glRotatef(angle,rot[M_X],rot[M_Y],rot[M_Z]);

        //rotate the camera z
        normalVector(rot,translation,rot2);
        glRotatef(angle2,rot2[M_X],rot2[M_Y],rot2[M_Z]);


        glLineWidth(1.0);
        glBegin(GL_LINES);

            glColor3f(1.0, 0.0, 0.0);
                glVertex3iv(bx);
                glVertex3iv(c);
                glVertex3iv(c);
                glVertex3iv(ex);

                glVertex3iv(bxby);
                glVertex3iv(by);
                glVertex3iv(by);
                glVertex3iv(exby);

                glVertex3iv(bxey);
                glVertex3iv(ey);
                glVertex3iv(ey);
                glVertex3iv(exey);

            glColor3f(0.0, 1.0, 0.0);
                glVertex3iv(by);
                glVertex3iv(c);
                glVertex3iv(c);
                glVertex3iv(ey);

                glVertex3iv(bxby);
                glVertex3iv(bx);
                glVertex3iv(bx);
                glVertex3iv(bxey);

                glVertex3iv(exby);
                glVertex3iv(ex);
                glVertex3iv(ex);
                glVertex3iv(exey);

            glColor3f(0.0, 0.0, 1.0);
                glVertex3iv(bz);
                glVertex3iv(c);
                glVertex3iv(c);
                glVertex3iv(ez);

        glEnd();

	glPopMatrix();
}


 void normalVector(float e1[],float e2[],float n[]){
      float t;
      n[M_X]=e1[M_Y]*e2[M_Z] - e1[M_Z]*e2[M_Y];
      n[M_Y]=e1[M_Z]*e2[M_X] - e1[M_X]*e2[M_Z];
      n[M_Z]=e1[M_X]*e2[M_Y] - e1[M_Y]*e2[M_X];
      t=sqrt(n[M_X]*n[M_X]+n[M_Y]*n[M_Y]+n[M_Z]*n[M_Z]);

      if(t!=0){
           n[M_X]/=t;
           n[M_Y]/=t;
           n[M_Z]/=t;
      }
 }

// Function name	: createMoon
// Description	    : creates a moon and adds it to the global list and returns a pointer
// Return type		: extern void
// Argument         : moon_data* moon
extern moon* createMoon(moon_data* moonData){
	moon* m;

	m=new moon(moonData);
	moon_list[nMoons]=m;
	nMoons++;

	return m;
}

// Function name	: createPlanet
// Description	    : creates a planet and his moons and adds it to the global list.
// Return type		: extern void
// Argument         : planet_data* planet
extern void createPlanet(planet_data* planetData){
	int count;

	planet_list[nPlanets]=new planet(planetData);
	nPlanets++;

	for(count=0;count<planetData->numberOfMoons;count++){
		moon_list[nMoons]=planetData->moon_list[count];
		nMoons++;
	}
}

// Function name	: createUniverse
// Description	    : defines/creates all the planets and moons
// Return type		: extern void
extern void createUniverse(){
	float			rotation[3];
	float			translation[3];
	float			color[3];
	float			speed;
	float			size;

	int				numberOfMoons;

	moon*			planet_moons[MAX_MOONS];
	planet_data*	pData;
	moon_data*		mData;
	model*			shape;



	nPlanets=0;
	nMoons=0;

	load_ModelList(model_list[0]);

	createSun();
	createMercurius();
	createVenus();
	createEarth();
	createMars();
	createJupiter();
	createSaturnus();
	createUranus();
	createNeptunus();
	createPluto();

}

// Function name	: moveUniverse
// Description	    : moves every planet/moon according to their rotation and speed
// Return type		: extern void
extern void moveUniverse(){
	int count;
	for(count=0;count<nPlanets;count++){
		planet_list[count]->rotate(rotFactor);
	}
	for(count=0;count<nMoons;count++){
		moon_list[count]->rotate(rotFactor);
	}
}

// Function name	: drawPlanet
// Description	    : draw planet number 'planet_number'  and his moons
// Return type		: extern void
// Argument         : int planet_number
// NOTE				: make sure you saved the current matrix before loading this function.
extern void drawPlanet(int planet_number){
	float rot[3];
	float tra[3];
	float color[3];
	float ang;
	float size;
	float ambient[4];
	int nM;
	int count;
	planet* p=planet_list[planet_number];
	moon* m;

    //get the rotation/translation of the planet and the number of moons
	p->getTranslation(tra);
	p->getRotation(rot);
	p->getColor(color);
	ang=p->getAngle();
	size=p->getSize();
	nM=p->getMoonN();

	//translate/rotate the matrix of the planet
	glRotatef(ang,rot[M_X],rot[M_Y],rot[M_Z]);
	glTranslatef(tra[M_X]*transFactor,tra[M_Y]*transFactor,tra[M_Z]*transFactor);


	//colors
	ambient[0]=color[0];
	ambient[1]=color[1];
	ambient[2]=color[2];
	ambient[3]=1.0;
	printf("color = %f %f %f\n",color[0],color[1],color[2]);
	//glLightfv(GL_LIGHT0, GL_AMBIENT,ambient);
	//glColor3fv(color);
	glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,ambient);


	///scale&plot
	glPushMatrix();
        glScalef(size*scaleFactor,size*scaleFactor,size*scaleFactor);
        drawPolygonModel(p->getModel());
	glPopMatrix();


	//moon gedeelte

	for(count=0;count < nM;count++){
		m=p->getMoon(count);
		m->getTranslation(tra);
		m->getRotation(rot);
		m->getColor(color);
		ang=m->getAngle();
		size=m->getSize();

		//translate/rotate the matrix of the moon
		glPushMatrix();
		glRotatef(ang,rot[M_X],rot[M_Y],rot[M_Z]);
		glTranslatef(tra[M_X]*transFactor,tra[M_Y]*transFactor,tra[M_Z]*transFactor);

        //colors
		ambient[0]=color[0];
		ambient[1]=color[1];
		ambient[2]=color[2];
		ambient[3]=1.0;
		//glLightfv(GL_LIGHT0, GL_AMBIENT,ambient);
		//glColor3fv(color);
		glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,ambient);
       ///scale&plot
		glPushMatrix();
		glScalef(size*scaleFactor,size*scaleFactor,size*scaleFactor);
		drawPolygonModel(m->getModel());
		glPopMatrix();



		glPopMatrix();
    }


}


// Function name	: drawUniverse
// Description	    : draws the complete universe
// Return type		: extern void
extern void drawUniverse(){
	int count;
	float rot[3],rot2[3];
	float translation[3]={eyeX,eyeY,eyeZ};

	glPushMatrix();

        glLoadIdentity();

        gluLookAt(zoomFactor*eyeX,zoomFactor*eyeY,zoomFactor*eyeZ,centerX,centerY,centerZ,upX,upY,upZ);

        //rotate the camera x-y
        rotationNormal(translation,rot);
        glRotatef(angle,rot[M_X],rot[M_Y],rot[M_Z]);

        //rotate the camera z
        normalVector(rot,translation,rot2);
        glRotatef(angle2,rot2[M_X],rot2[M_Y],rot2[M_Z]);


            glShadeModel (GL_SMOOTH);
    /*********************************************************
     *             LIGHT STUFF                               *
     *********************************************************/
        GLfloat light_ambient[] = {0.7,0.7,0.0,1.0};
        GLfloat light_diffuse[] = {0.7,0.7,0.5, 1.0};
        GLfloat light_specular[] = {0.7,0.7,0.8, 1.0 };
        GLfloat light_position[] = { 0.0, 0.0,7.0, 0.0 };

        glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
        glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
        glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
        glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    /*********************************************************/

        glEnable(GL_LIGHTING);
        glEnable(GL_LIGHT0);

            //draw the sun , this will be the center of the universe
            if(nPlanets>0)
                drawPlanet(0);


            //draw the rest of the universe
            for(count=1;count<nPlanets;count++){
                glPushMatrix();
                drawPlanet(count);
                glPopMatrix();
            }

        glDisable(GL_LIGHTING);
        glDisable(GL_LIGHT0);


        glFlush();
	glPopMatrix();
}

// Function name	: resetUniverse
// Description	    : reset all the planets/moons to their starting positions
// Return type		: extern void
extern void resetUniverse(){
	int count;
	for(count=0;count<nPlanets;count++)
		planet_list[count]->reset();

	for(count=0;count<nMoons;count++)
		moon_list[count]->reset();

}

// Function name	: drawModel
// Description	    : draws the polygons of the given model
// Return type		: extern void
// Argument         : p->getModel(
extern void drawPolygonModel(model* m){
	int count;

	glBegin(GL_TRIANGLES);

	for(count=0; count< m->numberOfFaces;count++){
		glNormal3fv(m->vertices[m->faces[count].vertex[0]].normal);
		glVertex3fv(m->vertices[m->faces[count].vertex[0]].pos);
		glNormal3fv(m->vertices[m->faces[count].vertex[1]].normal);
		glVertex3fv(m->vertices[m->faces[count].vertex[1]].pos);
		glNormal3fv(m->vertices[m->faces[count].vertex[2]].normal);
		glVertex3fv(m->vertices[m->faces[count].vertex[2]].pos);
	}
	glEnd();

}



// Function name	: fillMf3
// Description	    : fills a 3d dimensional float array with x,y &z
// Return type		: extern void
// Argument         : float* M
// Argument         : float x
// Argument         : float y
// Argument         : float z
extern void fillMf3(float* M,float x,float y,float z){
	if(M==NULL){
		fprintf(stderr,"Die you mtf !!!\n");
		exit(-1);
	}
	M[M_X]=x;
	M[M_Y]=y;
	M[M_Z]=z;
}



// Function name	: zoom
// Description	    : multiplies the current zoomFactor with factor
// Return type		: void
// Argument         : float factor
void zoom(float factor){
	zoomFactor*=factor;
}


// Function name	: rotationNormal
// Description	    : determines the normal needed to rotate ,
//                    given a translation
// Return type		: void
// Argument         : float trans[]
// Argument         : float* rotation
void rotationNormal(float trans[],float* rotation){
	float e1[3],e2[3],e3[3];
	fillMf3(e1,trans[M_X],trans[M_Y],trans[M_Z]);
	if(trans[M_Z]!=0)
		fillMf3(e2,trans[M_X],trans[M_Y],0.0);
	else
		fillMf3(e2,0.0,0.0,1.0);
	normalVector(e1,e2,e3);
	normalVector(e1,e3,rotation);
}


// Function name	: normalVector2
// Description	    : an adaptation of normalVector, this does the same,
//                    except for scaling the vector to 1
// Return type		: void
// Argument         : float e1[]
// Argument         : float e2[]
// Argument         : float n[]
void normalVector2(float e1[],float e2[],float n[]){
	n[M_X]=e1[M_Y]*e2[M_Z] - e1[M_Z]*e2[M_Y];
	n[M_Y]=e1[M_Z]*e2[M_X] - e1[M_X]*e2[M_Z];
	n[M_Z]=e1[M_X]*e2[M_Y] - e1[M_Y]*e2[M_X];
}

void turnCamera(float dAngle){
	angle+=dAngle;
}

void turnCameraZ(float dAngle){
	angle2+=dAngle;
}

// Function name	: setScaleFactor
// Description	    : sets the scaling factor
// Return type		: void
// Argument         : float sFactor
void setScaleFactor(float sFactor){
	scaleFactor=sFactor;
}

// Function name	: setTransFactor
// Description	    : sets the translation factor
// Return type		: void
// Argument         : float tFactor
void setTransFactor(float tFactor){
	transFactor=tFactor;
}


// Function name	: setRotFactor
// Description	    : sets the rotation factor
// Return type		: void
// Argument         : float rFactor
void setRotFactor(float rFactor){
	rotFactor=rFactor;
}


int main(int argc, char *argv[]) {
	nModels=0;
	printf("v1.0 alpha\n");
	if (!readModel("geosphere.cgm")) {
		fprintf(stderr, "Could not read model\n");
		exit(-1);
	}

	createUniverse();

	// maak het window aan
	createWindow(1, argv);



	// zet de positie van het figuur
	reset();

	// start de mainloop
	return Fl::run();
}
